#!/usr/bin/env python

# ansible_cmd
#
# Generate host overview (configuration management database) from ansible fact
# gathering output.
#
# Usage:
#
#   $ ansible -m setup --tree out all
#   $ ansible-cmdb out > cmdb.html
#

import optparse
import sys
import os
import logging

import ansiblecmdb
from mako.template import Template
from mako import exceptions


if __name__ == "__main__":
    # Set up logging
    root = logging.getLogger()
    root.setLevel(logging.CRITICAL)
    ch = logging.StreamHandler(sys.stderr)
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(message)s')
    ch.setFormatter(formatter)
    root.addHandler(ch)
    log = root

    parser = optparse.OptionParser(version="%prog v%%MASTER%%")
    parser.set_usage(sys.argv[0] + " [option] <dir> > output.html")
    parser.add_option("-t", "--template", dest="template", action="store", default='html_fancy', help="Template to use. Default is 'html_fancy'")
    parser.add_option("-i", "--inventory", dest="inventory", action="store", default=None, help="Inventory to read extra info from")
    parser.add_option("-f", "--fact-cache", dest="fact_cache", action="store_true", default=False, help="<dir> contains fact-cache files")
    parser.add_option("-p", "--params", dest="params", action="store", default=None, help="Params to send to template")
    parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="Show debug output")
    parser.add_option("-c", "--columns", dest="columns", action="store", default=None, help="Show only given columns")
    (options, args) = parser.parse_args()

    if len(args) < 1:
        parser.print_usage()
        sys.stderr.write("The <dir> argument is mandatory\n")
        sys.exit(1)

    if options.debug:
        root.setLevel(logging.DEBUG)

    # Find out our installation prefix
    data_dir = os.path.join(os.path.dirname(ansiblecmdb.__file__), 'data')
    tpl_dir = os.path.join(data_dir, 'tpl')
    static_dir = os.path.join(data_dir, 'static')
    log.debug('data_dir = {0}'.format(data_dir))
    log.debug('tpl_dir = {0}'.format(tpl_dir))
    log.debug('static_dir = {0}'.format(static_dir))

    # Find configuration file to use. Params override optiosn for this
    config_file = None
    config_locations = ['/etc/ansible/ansible.cfg', 'ansible.cfg']

    for config_location in config_locations:
        if os.path.exists(config_location):
            config_file = os.path.realpath(config_location)

    log.debug('config_file = {0}'.format(config_file))
    if config_file:
        with open(config_file, 'r') as cf:
            for line in cf:
                if line.startswith('hostfile'):
                    options.inventory = line.split('=', 1)[1].strip()
    log.debug('inventory hostfile = {0}'.format(options.inventory))

    # Handle template params
    params = {
        'lib_dir': data_dir,  # Backwards compatibility for custom templates < ansible-cmdb v1.7
        'data_dir': data_dir,
        'version': '%%MASTER%%',
        'columns': None,
    }
    if options.params:
        try:
            for param in options.params.split(','):
                param_key, param_value = param.split('=', 1)
                params[param_key] = param_value
        except ValueError as e:
            sys.stdout.write("Invalid params specified. Should be in format: <key=value>[,<key=value>..]\n")
            sys.exit(1)
        log.debug(params)
    if options.columns is not None:
        params['columns'] = options.columns.split(',')

    ansible = ansiblecmdb.Ansible(args, options.inventory, options.fact_cache, debug=options.debug)

    # Render a template with the gathered host info
    if os.path.isfile(options.template):
        # --template option points to an actual file
        log.debug('template file = {0}'.format(options.template))
        mytemplate = Template(filename=options.template, default_filters=['decode.utf8'], input_encoding='utf-8', output_encoding='utf-8')
    else:
        # Find template given with --template in the standard templates dir.
        tpl_dir = os.path.join(os.path.dirname(ansiblecmdb.__file__), 'data', 'tpl')
        tpl_path = os.path.join(tpl_dir, '%s.tpl' % (options.template))
        if not os.path.isfile(tpl_path):
            sys.stderr.write("Template not found: {0}\n".format(tpl_path))
            sys.exit(1)
        log.debug('template file = {0}'.format(tpl_path))
        mytemplate = Template(filename=tpl_path, default_filters=['decode.utf8'], input_encoding='utf-8', output_encoding='utf-8')

    # Make sure we always output in UTF-8, regardless of the user's locale /
    # terminal encoding. This is different in Python 2 and 3.
    params['log'] = log
    log.debug("Template params: {0}".format(params))
    try:
        if sys.version_info[0] == 3:
            out = mytemplate.render(hosts=ansible.hosts, **params)
            sys.stdout.buffer.write(out)
        else:
            out = mytemplate.render(hosts=ansible.hosts, **params)
            sys.stdout.write(out)
    except Exception as err:
        if options.debug:
            sys.stderr.write(exceptions.text_error_template().render())
            sys.stderr.write('\n')
        debug_cmd = "{0} -d {1}".format(sys.argv[0], ' '.join(sys.argv[1:]))
        debug_txt = ("Whoops, it looks like something went wrong while rendering the template.\n\n"
                     "The reported error was: {0}\n\n"
                     "The output is probably not correct.\n".format(err))
        if not options.debug:
            debug_txt += ("You can re-run ansible-cmdb with the -d switch to turn on debugging\n"
                          "to get an insight in what might be going wrong:\n\n"
                          "  {0}\n\n".format(debug_cmd))
        debug_txt += \
"""\
You can report a bug on the issue tracker:

  https://github.com/fboender/ansible-cmdb/issues

Please include the debugging output (-d switch) in the report!

If you can, also include the hosts file and the facts file for the last host
that rendered properly ('Rendering host...' in the output. If these files must
remain confidential, you can send them to ferry.boender@gmail.com instead.
"""
        sys.stderr.write(debug_txt)
